Add a runtime create_model variant to the schema_validators example - #3148
Add a runtime create_model variant to the schema_validators example#3148Aaron-Oh wants to merge 2 commits into
Conversation
The story showed four ways to type a tool parameter (BaseModel, TypedDict, dataclass, dict). Add a fifth: a pydantic model built at runtime with create_model from an external JSON Schema dict, then handed to @mcp.tool() like any BaseModel. Covers the doc gap behind issues modelcontextprotocol#323, modelcontextprotocol#761, modelcontextprotocol#772. A create_model() result is opaque to static type checkers, so a TYPE_CHECKING branch aliases it to a same-shape declared model while the runtime uses the dynamic class. server_lowlevel.py, client.py and README.md are updated to include the new variant.
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- server.py: JSON Schema 'required' is optional, so default it to an empty list before membership testing. External schemas with only optional properties no longer raise KeyError at import time. - README.md: greet_dynamic publishes the same schema as greet_pydantic, so the client bullet now says four typed variants, not three.
|
Thanks for the review — addressed both in 230f718:
|
|
Hi maintainers, sorry for the small bump on this one. Just a gentle ping — CI is all green here, and the two cubic comments are already addressed in 230f718 (the missing I know the review queue is quite busy these days, so please take your time, no rush at all. But if this variant is not the direction you want for Thank you in advance! |
What
The
schema_validatorsexample story shows four ways to type a toolparameter so
MCPServerderives and enforcesinputSchema: a pydanticBaseModel, aTypedDict, a@dataclass, and a baredict[str, Any].This adds a fifth variant: a pydantic model built at runtime with
create_modelfrom an external JSON Schema dict, then handed to@mcp.tool()exactly like a hand-writtenBaseModel.Why
Issues #323, #761, and #772 all asked the same thing: how to drive a
tool's
inputSchemafrom a JSON Schema you already hold (from OpenAPI, aconfig file, a DB row) rather than a class written out in source. The
maintainer answer is "use a pydantic model as the parameter" — but the
example suite never showed how to get that model when it isn't declared
statically. This closes that documentation gap with a runnable variant.
Notes
create_model()result is opaque to static type checkers (its fieldsdon't exist until runtime, and a runtime variable can't appear in a type
annotation). A
TYPE_CHECKINGbranch aliases it to a same-shape declaredmodel so type checkers can see the fields; at runtime the dynamic class is
what
@mcp.tool()reflects over. This is called out in the README.greet_pydanticvariant — thepoint is purely how the model is obtained, not a different wire shape.
server_lowlevel.py,client.py, andREADME.mdare updated to coverthe new variant.
Validation
uv run --frozen ruff format --check/ruff check— cleanuv run --frozen pyright— 0 errorsuv run --frozen pytest tests/examples -k schema— 14 passed(in-memory/http × modern/legacy × server/server_lowlevel, plus the
manifest and story-shape checks)